<?php

require_once 'vendor/autoload.php';

use Smalot\Cups\Builder\Builder;
use Smalot\Cups\Manager\JobManager;
use Smalot\Cups\Manager\PrinterManager;
use Smalot\Cups\Model\Job;
use Smalot\Cups\Transport\ResponseParser;
use Http\Client\Socket\Client as HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * IPP打印故障排除器
 * 专门解决"IPP打印: ❌ 失败"问题的综合解决方案
 */
class IPPPrintTroubleshooter
{
    private string $printerIp;
    private int $printerPort;
    private Builder $builder;
    private ResponseParser $responseParser;
    private array $diagnosticResults = [];

    public function __construct(string $printerIp = '10.1.0.12', int $printerPort = 631)
    {
        $this->printerIp = $printerIp;
        $this->printerPort = $printerPort;
        $this->builder = new Builder();
        $this->responseParser = new ResponseParser();
        
        echo "🔧 IPP打印故障排除器\n";
        echo "目标打印机: {$this->printerIp}:{$this->printerPort}\n";
        echo str_repeat('=', 60) . "\n\n";
    }

    /**
     * 运行完整的故障排除流程
     */
    public function runFullDiagnostics(): bool
    {
        echo "🚀 开始IPP打印故障排除...\n\n";
        
        // 步骤1: 网络连接诊断
        $this->runNetworkDiagnostics();
        
        // 步骤2: 打印机服务检测
        $this->checkPrinterServices();
        
        // 步骤3: 尝试多种IPP连接方法
        $success = $this->attemptMultipleIPPMethods();
        
        // 步骤4: 如果IPP失败，尝试备用方案
        if (!$success) {
            $success = $this->tryFallbackMethods();
        }
        
        // 步骤5: 生成诊断报告
        $this->generateDiagnosticReport($success);
        
        return $success;
    }

    /**
     * 网络连接诊断
     */
    private function runNetworkDiagnostics(): void
    {
        echo "📡 步骤1: 网络连接诊断\n";
        echo str_repeat('-', 40) . "\n";
        
        // Ping测试
        echo "🔍 Ping测试: ";
        $pingResult = $this->pingTest();
        echo $pingResult ? "✅ 成功\n" : "❌ 失败\n";
        $this->diagnosticResults['ping'] = $pingResult;
        
        // 端口测试
        echo "🔌 端口631测试: ";
        $portResult = $this->portTest();
        echo $portResult ? "✅ 开放\n" : "❌ 关闭\n";
        $this->diagnosticResults['port'] = $portResult;
        
        // 网络延迟测试
        echo "⏱️  网络延迟测试: ";
        $latency = $this->measureLatency();
        if ($latency !== null) {
            echo "✅ {$latency}ms\n";
            $this->diagnosticResults['latency'] = $latency;
        } else {
            echo "❌ 超时\n";
            $this->diagnosticResults['latency'] = null;
        }
        
        echo "\n";
    }

    /**
     * 检查打印机服务
     */
    private function checkPrinterServices(): void
    {
        echo "🖨️  步骤2: 打印机服务检测\n";
        echo str_repeat('-', 40) . "\n";
        
        // IPP服务检测
        echo "🌐 IPP服务检测: ";
        $ippResult = $this->testIPPService();
        echo $ippResult['available'] ? "✅ 可用" : "❌ 不可用";
        if ($ippResult['available'] && $ippResult['response_time']) {
            echo " (响应时间: " . round($ippResult['response_time'] * 1000, 2) . "ms)";
        }
        echo "\n";
        $this->diagnosticResults['ipp_service'] = $ippResult;
        
        // Web界面检测
        echo "🌍 Web界面检测: ";
        $webResult = $this->testWebInterface();
        echo $webResult ? "✅ 可访问\n" : "❌ 无法访问\n";
        $this->diagnosticResults['web_interface'] = $webResult;
        
        echo "\n";
    }

    /**
     * 尝试多种IPP连接方法
     */
    private function attemptMultipleIPPMethods(): bool
    {
        echo "🔧 步骤3: 尝试多种IPP连接方法\n";
        echo str_repeat('-', 40) . "\n";
        
        $methods = [
            '标准IPP客户端' => [$this, 'tryStandardIPP'],
            '修复Host Header的IPP客户端' => [$this, 'tryFixedHostHeaderIPP'],
            '自定义Socket连接' => [$this, 'tryCustomSocketIPP'],
            '完整URI连接' => [$this, 'tryFullURIIPP']
        ];
        
        foreach ($methods as $methodName => $method) {
            echo "🧪 尝试{$methodName}: ";
            
            try {
                $result = call_user_func($method);
                if ($result) {
                    echo "✅ 成功\n";
                    $this->diagnosticResults['successful_method'] = $methodName;
                    echo "\n";
                    return true;
                } else {
                    echo "❌ 失败\n";
                }
            } catch (Exception $e) {
                echo "❌ 异常: " . $e->getMessage() . "\n";
                $this->diagnosticResults['errors'][$methodName] = $e->getMessage();
            }
        }
        
        echo "\n";
        return false;
    }

    /**
     * 尝试备用方案
     */
    private function tryFallbackMethods(): bool
    {
        echo "🆘 步骤4: 尝试备用打印方案\n";
        echo str_repeat('-', 40) . "\n";
        
        // Windows系统打印
        if (PHP_OS_FAMILY === 'Windows') {
            echo "🪟 Windows系统打印: ";
            $windowsResult = $this->tryWindowsPrint();
            echo $windowsResult ? "✅ 成功\n" : "❌ 失败\n";
            
            if ($windowsResult) {
                $this->diagnosticResults['fallback_method'] = 'Windows系统打印';
                echo "\n";
                return true;
            }
        } else {
            echo "ℹ️  当前系统不是Windows，跳过系统打印\n";
        }
        
        // 其他备用方案可以在这里添加
        
        echo "\n";
        return false;
    }

    /**
     * 标准IPP客户端
     */
    private function tryStandardIPP(): bool
    {
        $httpClient = new HttpClient();
        $jobManager = new JobManager($this->builder, $httpClient, $this->responseParser);
        $printerManager = new PrinterManager($this->builder, $httpClient, $this->responseParser);
        
        return $this->sendTestJob($jobManager, $printerManager);
    }

    /**
     * 修复Host Header的IPP客户端
     */
    private function tryFixedHostHeaderIPP(): bool
    {
        $customClient = new class($this->printerIp, $this->printerPort) extends HttpClient {
            private string $targetIp;
            private int $targetPort;
            
            public function __construct(string $ip, int $port)
            {
                parent::__construct();
                $this->targetIp = $ip;
                $this->targetPort = $port;
            }
            
            public function sendRequest(RequestInterface $request): ResponseInterface
            {
                // 修复Host header
                $request = $request
                    ->withHeader('Host', $this->targetIp . ':' . $this->targetPort)
                    ->withHeader('User-Agent', 'CUPS-IPP-Client/2.0')
                    ->withHeader('Accept', 'application/ipp')
                    ->withHeader('Content-Type', 'application/ipp')
                    ->withHeader('Accept-Language', 'en-us')
                    ->withHeader('Accept-Encoding', 'gzip, deflate')
                    ->withHeader('Connection', 'keep-alive');
                
                // 修改URI
                $uri = $request->getUri()
                    ->withHost($this->targetIp)
                    ->withPort($this->targetPort)
                    ->withScheme('http');
                
                $request = $request->withUri($uri);
                
                return parent::sendRequest($request);
            }
        };
        
        $jobManager = new JobManager($this->builder, $customClient, $this->responseParser);
        $printerManager = new PrinterManager($this->builder, $customClient, $this->responseParser);
        
        return $this->sendTestJob($jobManager, $printerManager);
    }

    /**
     * 自定义Socket连接
     */
    private function tryCustomSocketIPP(): bool
    {
        $socketOptions = [
            'remote_socket' => "tcp://{$this->printerIp}:{$this->printerPort}",
            'timeout' => 30,
            'stream_context_options' => [
                'socket' => [
                    'so_reuseport' => true,
                    'so_keepalive' => true
                ]
            ]
        ];
        
        $httpClient = new HttpClient(null, $socketOptions);
        $jobManager = new JobManager($this->builder, $httpClient, $this->responseParser);
        $printerManager = new PrinterManager($this->builder, $httpClient, $this->responseParser);
        
        return $this->sendTestJob($jobManager, $printerManager);
    }

    /**
     * 完整URI连接
     */
    private function tryFullURIIPP(): bool
    {
        $httpClient = new HttpClient();
        $jobManager = new JobManager($this->builder, $httpClient, $this->responseParser);
        $printerManager = new PrinterManager($this->builder, $httpClient, $this->responseParser);
        
        // 使用完整的URI格式
        $fullUri = "http://{$this->printerIp}:{$this->printerPort}/ipp/print";
        
        return $this->sendTestJob($jobManager, $printerManager, $fullUri);
    }

    /**
     * 发送测试打印任务
     */
    private function sendTestJob(JobManager $jobManager, PrinterManager $printerManager, ?string $customUri = null): bool
    {
        $printerUri = $customUri ?? "ipp://{$this->printerIp}:{$this->printerPort}/ipp/print";
        
        $printer = $printerManager->findByUri($printerUri);
        if (!$printer) {
            return false;
        }
        
        $job = new Job();
        $job->setName('IPP故障排除测试');
        $job->setUsername('troubleshooter');
        $job->setCopies(1);
        $job->setPrinterUri($printerUri);
        $job->addText($this->createTestContent());
        
        $result = $jobManager->send($printer, $job);
        return $result !== null;
    }

    /**
     * Windows系统打印
     */
    private function tryWindowsPrint(): bool
    {
        $testContent = $this->createTestContent();
        $tempFile = tempnam(sys_get_temp_dir(), 'ipp_troubleshoot_');
        file_put_contents($tempFile, $testContent);
        
        $printerNames = [
            'EPSON L4260 Series',
            'EPSON L4260',
            'L4260 Series',
            'EPSON L4260 Series (Copy 1)',
            'EPSON L4260 Series (Copy 2)'
        ];
        
        foreach ($printerNames as $printerName) {
            $command = "print /D:\"{$printerName}\" \"{$tempFile}\"";
            $output = [];
            $returnCode = 0;
            exec($command . ' 2>&1', $output, $returnCode);
            
            if ($returnCode === 0) {
                unlink($tempFile);
                return true;
            }
        }
        
        unlink($tempFile);
        return false;
    }

    /**
     * Ping测试
     */
    private function pingTest(): bool
    {
        $command = "ping -n 1 -w 1000 {$this->printerIp}";
        $output = [];
        $returnCode = 0;
        exec($command . ' 2>&1', $output, $returnCode);
        
        return $returnCode === 0;
    }

    /**
     * 端口测试
     */
    private function portTest(): bool
    {
        $connection = @fsockopen($this->printerIp, $this->printerPort, $errno, $errstr, 5);
        
        if ($connection) {
            fclose($connection);
            return true;
        }
        
        return false;
    }

    /**
     * 测量网络延迟
     */
    private function measureLatency(): ?float
    {
        $start = microtime(true);
        $connection = @fsockopen($this->printerIp, $this->printerPort, $errno, $errstr, 5);
        
        if ($connection) {
            $latency = (microtime(true) - $start) * 1000;
            fclose($connection);
            return round($latency, 2);
        }
        
        return null;
    }

    /**
     * 测试IPP服务
     */
    private function testIPPService(): array
    {
        $result = [
            'available' => false,
            'response_time' => null,
            'error' => null
        ];
        
        $start = microtime(true);
        
        try {
            $context = stream_context_create([
                'http' => [
                    'method' => 'POST',
                    'header' => [
                        'Content-Type: application/ipp',
                        'Host: ' . $this->printerIp . ':' . $this->printerPort,
                        'User-Agent: IPP-Troubleshooter/1.0'
                    ],
                    'content' => '',
                    'timeout' => 10
                ]
            ]);
            
            $response = @file_get_contents(
                "http://{$this->printerIp}:{$this->printerPort}/ipp/print", 
                false, 
                $context
            );
            
            $result['response_time'] = microtime(true) - $start;
            $result['available'] = true;
            
        } catch (Exception $e) {
            $result['error'] = $e->getMessage();
        }
        
        return $result;
    }

    /**
     * 测试Web界面
     */
    private function testWebInterface(): bool
    {
        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'timeout' => 10,
                'header' => 'User-Agent: IPP-Troubleshooter/1.0'
            ]
        ]);
        
        $response = @file_get_contents("http://{$this->printerIp}", false, $context);
        return $response !== false;
    }

    /**
     * 创建测试内容
     */
    private function createTestContent(): string
    {
        return "IPP打印故障排除测试\n" .
               "========================\n" .
               "时间: " . date('Y-m-d H:i:s') . "\n" .
               "打印机: {$this->printerIp}:{$this->printerPort}\n" .
               "测试类型: 故障排除\n" .
               "\n" .
               "如果您看到这个文档，说明打印功能已恢复正常！\n" .
               "\n" .
               "故障排除完成时间: " . date('Y-m-d H:i:s') . "\n";
    }

    /**
     * 生成诊断报告
     */
    private function generateDiagnosticReport(bool $success): void
    {
        echo "📊 诊断报告\n";
        echo str_repeat('=', 60) . "\n";
        
        // 网络状态
        echo "🌐 网络连接状态:\n";
        echo "   Ping测试: " . ($this->diagnosticResults['ping'] ? "✅ 成功" : "❌ 失败") . "\n";
        echo "   端口631: " . ($this->diagnosticResults['port'] ? "✅ 开放" : "❌ 关闭") . "\n";
        
        if (isset($this->diagnosticResults['latency'])) {
            $latency = $this->diagnosticResults['latency'];
            echo "   网络延迟: " . ($latency ? "{$latency}ms" : "超时") . "\n";
        }
        
        // 服务状态
        echo "\n🖨️  打印机服务状态:\n";
        $ippService = $this->diagnosticResults['ipp_service'];
        echo "   IPP服务: " . ($ippService['available'] ? "✅ 可用" : "❌ 不可用") . "\n";
        echo "   Web界面: " . ($this->diagnosticResults['web_interface'] ? "✅ 可访问" : "❌ 无法访问") . "\n";
        
        // 打印结果
        echo "\n🎯 打印测试结果:\n";
        if ($success) {
            echo "   状态: ✅ 成功\n";
            if (isset($this->diagnosticResults['successful_method'])) {
                echo "   成功方法: {$this->diagnosticResults['successful_method']}\n";
            }
            if (isset($this->diagnosticResults['fallback_method'])) {
                echo "   备用方法: {$this->diagnosticResults['fallback_method']}\n";
            }
        } else {
            echo "   状态: ❌ 失败\n";
        }
        
        // 错误信息
        if (isset($this->diagnosticResults['errors']) && !empty($this->diagnosticResults['errors'])) {
            echo "\n❌ 错误详情:\n";
            foreach ($this->diagnosticResults['errors'] as $method => $error) {
                echo "   {$method}: {$error}\n";
            }
        }
        
        // 建议
        echo "\n💡 建议:\n";
        if (!$this->diagnosticResults['ping']) {
            echo "   - 检查打印机IP地址是否正确\n";
            echo "   - 确认打印机已开机并连接网络\n";
        }
        
        if (!$this->diagnosticResults['port']) {
            echo "   - 检查防火墙设置，确保端口631开放\n";
            echo "   - 确认打印机IPP服务已启用\n";
        }
        
        if (!$ippService['available']) {
            echo "   - 访问打印机Web界面检查IPP设置\n";
            echo "   - 尝试重启打印机网络服务\n";
        }
        
        if (!$success) {
            echo "   - 考虑使用USB连接作为临时解决方案\n";
            echo "   - 联系网络管理员检查网络配置\n";
            echo "   - 查看打印机固件是否需要更新\n";
        }
        
        echo "\n" . str_repeat('=', 60) . "\n";
        
        if ($success) {
            echo "🎉 IPP打印问题已解决！\n";
        } else {
            echo "😞 IPP打印问题未能解决，请参考上述建议进行进一步排查。\n";
        }
    }
}

// 运行故障排除
try {
    // 可以修改IP地址为您的打印机IP
    $troubleshooter = new IPPPrintTroubleshooter('10.1.0.12', 631);
    $success = $troubleshooter->runFullDiagnostics();
    
    exit($success ? 0 : 1);
    
} catch (Exception $e) {
    echo "❌ 故障排除器运行失败: {$e->getMessage()}\n";
    echo "📍 位置: {$e->getFile()}:{$e->getLine()}\n";
    exit(1);
}